using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using BigDoc.Filter;
using Speed;

namespace SpeedSearch.FullText.Parsing
{

    /// <summary>
    /// Classe de parser de textos
    /// </summary>
    public class Parser
    {

        private Dictionary<string, ISearchFilter> filters;
        Dictionary<string, string> indexExtensions;
        Dictionary<string, string> notIndexExtensions;

        public Parser(Dictionary<string, string> indexExtensions, Dictionary<string, string> notIndexExtensions)
        {
            this.indexExtensions = indexExtensions;
            this.notIndexExtensions = notIndexExtensions;
            LoadFilters();
        }

        [DllImport("query.dll", CharSet = CharSet.Unicode)]
        private extern static int LoadIFilter(string pwcsPath, ref IUnknown pUnkOuter, ref IFilter ppIUnk);

        [ComImport, Guid("00000000-0000-0000-C000-000000000046")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        private interface IUnknown
        {
            [PreserveSig]
            IntPtr QueryInterface(ref Guid riid, out IntPtr pVoid);

            [PreserveSig]
            IntPtr AddRef();

            [PreserveSig]
            IntPtr Release();
        }


        private IFilter getFilter(string filename)
        {
            IUnknown iunk = null;
            IFilter filter = null;

            // Try to load the corresponding IFilter 
            int resultLoad = LoadIFilter(filename, ref iunk, ref filter);
            if (resultLoad != (int)IFilterReturnCodes.S_OK)
            {
                return null;
            }
            return filter;
        }

        private void LoadFilters()
        {
            string pluginsDir = System.IO.Directory.GetParent(Assembly.GetExecutingAssembly().Location) + "\\Filters";
            DirectoryInfo di = new DirectoryInfo(pluginsDir);
            filters = new Dictionary<string, ISearchFilter>();

            if (!di.Exists)
                return;

            FileInfo[] files = di.GetFiles("*.dll");
            foreach (FileInfo fi in files)
            {
                try
                {
                    LoadFilter(fi.FullName);
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Parser.LoadFilters: " + fi.FullName);
                }
            }
        }

        private void LoadFilter(string path)
        {
            Assembly testasm = Assembly.LoadFrom(path);
            Type[] types = testasm.GetTypes();
            foreach (Type type in types)
            {
                if (type.IsClass & type.GetInterface("ISearchFilter") != null)
                {
                    object o = testasm.CreateInstance(type.FullName);
                    ISearchFilter filter = o as ISearchFilter;
                    if (filter == null)
                        return;

                    foreach (string extension in filter.Extensions)
                    {
                        string ext = extension.ToLower();
                        if (!filters.ContainsKey(ext))
                            filters.Add(ext, filter);
                    }
                }
            }
        }

        //public string Parse(string filename, out Data.EnumIndexItemStatus status, out Exception exception)
        public string Parse(string filename, out Exception exception)
        {
            IFilter filter = null;
            exception = null;

            try
            {
                string ext = Path.GetExtension(filename).ToLower();
#if DEBUG
                if (ext == ".pdf")
                    ToString();
#endif

                //if (ext == ".docx")
                //{
                //    DocxToText doc = new DocxToText(filename);
                //    string text = doc.ExtractText();
                //    status = Data.EnumItemIndexStatus.Processed;
                //    return text;
                //}

                if (notIndexExtensions.ContainsKey(ext))
                {
                    //status = Data.EnumIndexItemStatus.NotIndexed;
                    return null;
                }

                if (indexExtensions.ContainsKey(ext))
                {
                    //status = Data.EnumIndexItemStatus.Processed;
                    return System.IO.File.ReadAllText(filename);
                }

                // se existir um filter, usa-o
                if (filters.ContainsKey(ext))
                {
                    try
                    {
                        //status = Data.EnumIndexItemStatus.Processed;
                        return filters[ext].Extract(filename);
                    }
                    catch (Exception ex)
                    {
                        //status = Data.EnumIndexItemStatus.Error;
                        exception = ex;
                        return null;
                    }
                }


                bool use1 = false;

                if (use1)
                {
                    filter = getFilter(filename);

                    if (filter == null)
                    {
                        //status = Data.EnumIndexItemStatus.NotIndexed;
                        return null;
                    }

                    StringBuilder plainTextResult = new StringBuilder();
                    STAT_CHUNK ps = new STAT_CHUNK();
                    IFILTER_INIT mFlags = 0;

                    uint i = 0;
                    filter.Init(mFlags, 0, null, ref i);

                    int resultChunk = 0;

                    resultChunk = filter.GetChunk(out ps);
                    while (resultChunk == 0)
                    {
                        if (ps.flags == CHUNKSTATE.CHUNK_TEXT)
                        {
                            uint sizeBuffer = 60000;
                            int resultText = 0;
                            while (resultText == Constants.FILTER_S_LAST_TEXT || resultText == 0)
                            {
                                sizeBuffer = 100 * 1024;
                                System.Text.StringBuilder sbBuffer = new System.Text.StringBuilder((int)(10000 * sizeBuffer));
                                //System.Text.StringBuilder sbBuffer = new System.Text.StringBuilder();
                                resultText = filter.GetText(ref sizeBuffer, sbBuffer);

                                if (sizeBuffer > 0 && sbBuffer.Length > 0)
                                {
                                    string chunk = sbBuffer.ToString(0, (int)sizeBuffer);
                                    plainTextResult.Append(chunk);
                                }
                            }
                        }
                        resultChunk = filter.GetChunk(out ps);
                    }
                    //status = Data.EnumIndexItemStatus.Processed;
                    return plainTextResult.ToString();
                }
                else
                {
                    using (var target = new EPocalipse.IFilter.FilterReader(filename))
                    {
                        if (target != null)
                        {
                            //status = Data.EnumIndexItemStatus.Processed;
                            return target.ReadToEnd();
                        }
                        else
                        {
                            //status = Data.EnumIndexItemStatus.NotIndexed;
                            return null;
                        }
                    }

                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (filter != null)
                    Marshal.ReleaseComObject(filter);
            }
        }

    }

}
